class TableBodyBuilder:

    def __init__(self, ordered_data: list, related_columns=None, style_func=None):
        self.data = ordered_data
        self.related_columns = related_columns
        self.style_func = style_func
        self.body = ['<tbody>']
        self.columns_rowspan = [0] * len(self.data[0])
        self.print_flags = [True] * len(self.columns_rowspan)
        self.print_row_num = 0

    def generate_table(self):
        while self.print_row_num < len(self.data):
            self.body.append('<tr>')
            self.body.append(self.get_row_html())
            self.body.append('</tr>')
            self.print_row_num += 1
        self.body.append('</tbody>')
        return ''.join(self.body)

    def get_row_html(self):
        res = []
        row = self.data[self.print_row_num]

        # 当前行各列跨行值的计算
        for index in range(len(row)):
            if self.columns_rowspan[index] < 1:
                self.columns_rowspan[index] = self.get_column_rowspan(index)
                self.minimize_related_columns(index + 1)
                self.print_flags[index] = True

        for index, value in enumerate(row):
            prop = ''

            if self.style_func and index in self.style_func:
                prop = ' ' + self.style_func[index](value)

            if self.print_flags[index]:
                rowspan = self.columns_rowspan[index]
                if rowspan > 1:
                    res.append(f'<td{prop} rowspan={rowspan}>{value}</td>')
                elif rowspan == 1:
                    res.append(f'<td{prop}>{value}</td>')
                self.print_flags[index] = False
            self.columns_rowspan[index] -= 1

        return ''.join(res)

    def get_column_rowspan(self, column_order):
        prev_value = None
        rowspan = 0
        for row in self.data[self.print_row_num:]:
            if prev_value is None or prev_value == row[column_order]:
                rowspan += 1
                prev_value = row[column_order]
            else:
                break
        return rowspan

    def minimize_related_columns(self, end_column_order):
        if not self.related_columns:
            return

        for relates in self.related_columns:
            min_rowspan = self.columns_rowspan[relates[0]]
            for i in relates:
                if i < end_column_order:
                    min_rowspan = min(min_rowspan, self.columns_rowspan[i])

            for j in relates:
                if j < end_column_order:
                    self.columns_rowspan[j] = min_rowspan
